"""
检查并更新已安装的 Python 包。脚本会检查已安装的包是否有更新，如果有，会提示用户是否升级，并在升级后显示更新后的版本号。
如果没有包需要更新，脚本将显示所有已安装的包的名称和版本号
"""

import subprocess        # 导入 subprocess 模块，用于执行命令
from win10toast import ToastNotifier    # 导入 ToastNotifier 模块，用于弹出 Windows 下的提示窗口


def get_outdated_packages():
    # 获得所有当前安装的过期的包
    packages = []    # 创建一个空的列表，用于保存过期的包名和版本
    result = subprocess.run(['pip', 'list', '--outdated', '--trusted-host', 'mirrors.aliyun.com'], stdout=subprocess.PIPE)
    # 调用 subprocess 模块中的 run() 函数，获取已过期的包
    # --trusted-host mirrors.aliyun.com 参数是为了使用阿里云镜像源加速下载速度
    for line in result.stdout.decode('utf-8').split('\n'):
        if line.startswith('Package:'):    # 如果这一行是包名字和版本的信息
            package_info = line.split(' ')
            package_name = package_info[1]     # 包的名字信息在第二个位置
            package_version = package_info[3]  # 包的版本信息在第四个位置
            packages.append((package_name, package_version))    # 添加到包信息列表中
    return packages    # 返回包信息列表


def update_packages(packages):
    # 升级指定的包
    notifier = ToastNotifier()    # 创建 ToastNotifier 对象用于显示通知，initialized=True 表示自动尝试初始化一次，否则需要手动调用 .initialize() 方法
    updated = False    # 创建 updated 变量用于记录是否有包被更新
    count = len(packages) # 获取需要升级的包的数量
    for i, package in enumerate(packages):
        package_name, package_version = package    # 将包名和版本分别赋值给变量
        subprocess.run(['pip', 'install', '--upgrade', package_name, '--trusted-host', 'mirrors.aliyun.com'])    # 使用 pip 执行命令升级指定的包
        # --trusted-host mirrors.aliyun.com 参数是为了使用阿里云镜像源加速下载速度
        installed_version = subprocess.check_output(['pip', 'show', package_name]).decode('utf-8').split('\n')[1].split(' ')[1]    # 获取升级后的包的版本信息
        if installed_version == package_version:    # 如果包的版本信息没有变化
            notifier.show_toast('Package Update', f'{package_name} {package_version} failed to update', duration=5)    # 弹出 Windows 提示窗口，提示升级失败
            print(f'{i + 1}/{count} - {package_name} {package_version} 升级失败') # 打印升级失败信息
        else:    # 如果包的版本信息有变化
            updated = True    # 更新 updated 变量为 True
            notifier.show_toast('Package Update', f'{package_name} {package_version} updated to {installed_version}', duration=5)    # 弹出 Windows 提示窗口，提示升级成功
            print(f'{i + 1}/{count} - {package_name} {package_version} 升级成功') # 打印升级成功信息
    if not updated:    # 如果没有包需要被升级
        print('所有包已经是最新版')
        installed_packages = subprocess.check_output(['pip', 'list']).decode('utf-8').split('\n')[2:-1]  # 获取所有安装的包的信息
        for package in installed_packages:
            package_name, _, package_version = package.split()  # 将包信息分割成名称、版本、描述三部分
            print(f'{package_name}: {package_version}')  # 打印包的名称和版本
    else:    # 如果有包需要被升级
        print('以下包已更新：')    # 打印提示信息
        for package in packages:    # 遍历所有被升级的包
            package_name, package_version = package    # 将包名和版本分别赋值给变量
            installed_version = subprocess.check_output(['pip', 'show', package_name]).decode('utf-8').split('\n')[1].split(' ')[1]    # 获取升级后的包的版本信息
            if installed_version != package_version:    # 如果包的版本信息发生改变
                print(f'{package_name}: {package_version} -> {installed_version}')    # 打印升级前后的版本信息


if __name__ == '__main__':
    packages = get_outdated_packages()    # 调用 get_outdated_packages() 函数，返回过期的包列表
    if packages:
        print(f'发现 {len(packages)} 个过期的包：')
        for package in packages:    # 打印过期的包名和版本
            package_name, package_version = package
            print(f'{package_name} {package_version}')
        print()    # 打印空行 
        action = input(f'是否升级这 {len(packages)} 个过期的包(Y/N):')
        if action.lower() == 'y':
            update_packages(packages)    # 调用 update_packages() 函数，升级过期的包
        else:
            print('用户取消升级')    # 提示用户取消升级
    else:
        print('所有包已经是最新版')    # 打印提示信息
        installed_packages = subprocess.check_output(['pip', 'list']).decode('utf-8').split('\n')[2:-1]  # 获取所有安装的包的信息
        for package in installed_packages:
            package_name, package_version = package.split(maxsplit=1)  # 将包信息分割成名称和版本两部分，限制分割次数为1
            print(f'{package_name}: {package_version}')  # 打印包的名称和版本